iT邦幫忙

2022 iThome 鐵人賽

DAY 7
0
Software Development

Rails Active Model系列 第 7

D-7 attribute 預設值不能直接使用容器型別

  • 分享至 

  • xImage
  •  

設定 attribute 預設值為容器型別,接著建立實體時不傳值,讓物件套用預設值,
接著對這個預設值物件進行操作,會污染到 class 原本設定的預設值。

不一定是容器型別,只要是任何可以修改裡面 value 的 object 都會有這情形。
下面來做個實驗:

class MyClass
  include ActiveModel::Model
  include ActiveModel::Attributes

  attribute :body, default: []
  # 設定一個 array 作為預設值
end

obj1 = MyClass.new
obj2 = MyClass.new

# obj1 往 body 裡面塞值,很正常的操作
obj1.body << 'head'
obj1.body
 => ["head"]

obj2.body
 => ["head"] 
# 神奇的事發生了,我改了 obj1 的東西,但 obj2 也連帶被影響到

obj3 = MyClass.new
obj3.body
 => ["head"]
# 甚至連後來新建的物件也被影響

會發生這種事是因為,當我們在 class 設定預設值的時候,傳入了一個要作為預設值的物件(這邊是用[])。

那後續這個作為預設值的物件,就會在 class 建立實體的時候從 class 本體被拉出來。

且不管你 new 幾次實體,只要有使用到預設值,那個跑出來的預設值永遠都會是同一個 object,同一組 object_id,同樣的記憶體位置!

那讀者或許會想,但我還是想要空的 array 做為預設值啊,那該怎麼做呢?

你需要把 array 包在 block 裡面傳進去

For example:

class MyClass
  include ActiveModel::Model
  include ActiveModel::Attributes

  attribute :body, default: -> { [] }
  # 改用block 包住 array 作為預設值
end

obj1 = MyClass.new
obj2 = MyClass.new

# obj1 往 body 裡面塞值,一樣的操作
obj1.body << 'head'
obj1.body
 => ["head"]

obj2.body
 => [] 
# obj2 的預設值正常了

obj3 = MyClass.new
obj3.body
 => []
# 後來新建的物件的預設值也正常了

總結,如果你想要設定容器型別作為預設值,就要先用 block 包住,才能確保對預設值那個物件的操作不會回過頭來影響到 class 本身~。


上一篇
D-6 傳入的 attribute 不會被 assign 為 instance variable !
下一篇
D-8 Active Model 本身有什麼功能? - validation
系列文
Rails Active Model28
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言